package xj.graph2d.tools;

import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Observer;
import java.util.Set;

import xj.graph2d.Command;
import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeConstants;
import xj.graph2d.GraphDocument;
import xj.graph2d.Group;
import xj.graph2d.RectangularGroup;
import xj.graph2d.command.DeltaCommand;
import xj.graph2d.command.GroupCommand;
import xj.graph2d.command.MoveCommand;
import xj.graph2d.command.RemoveShapeCommand;
import xj.util.gui.Point2DFloat;

public class GroupTool extends SimpleDrawTool {

  public GroupTool() {
    tempGroup = new RectangularGroup();
  }

  @Override
  public void mouseLButtonPressed(Observer observer, Point p, int modifiers) {
    if (debug) {
      System.out.println("GroupTool.mouseLButtonPressed p=" + p);
    }

    mouseButtonDown = true;
    adjacentEdges = null;
    editingGraph = false;
    graph = null;

    start(observer);
    if (view != null) {
      if (doc instanceof GraphDocument) {
	graph = (GraphDocument) doc;
	editingGraph = true;
      }

      Graphics2D g2 = canvas.getGraphics2D();
      canvas.setRenderingHints(g2);
      canvas.setTransform(g2);
      canvas.clearRepaintShapes();
      prev = view.physicalToVirtual(p);
      start = new Point2DFloat((float) prev.getX(), (float) prev.getY());

      DrawShape prevSelectedShape = doc.getSelectedShape();
      if (prevSelectedShape != null) {
	doc.setSelectedShape(null);
      }

      Group g = doc.getGroup();
      prevGroup = null;
      if (g != null && g.isInside(prev) == DrawShapeConstants.PointInside) {
	// move an exisiting group
	view.displayHelpMessage(MSGKEY_3);
	group = g;

	if (editingGraph) {
	  adjacentEdges = GraphDocument.allAdjacentEdges(g.allShapes());
	}

	Rectangle2D r = g.getRepaintRect();
	doc.setGroup(null);
	canvas.addRepaintShape(group);
      } else {
	view.displayHelpMessage(MSGKEY_2);
	// create a new group
	group = null;
	float x0 = (float) start.getX();
	float y0 = (float) start.getY();
	tempGroup.setBounds(x0, y0, x0, y0);
	if (g != null) {
	  Rectangle2D r = g.getBounds();

	  if (debug) {
	    System.out.println("GroupTool.mouseLButtonPressed() before ungroup: doc count=" + doc.count() + 
		" group count=" + g.getShapeCount());
	  }

	  prevGroup = doc.ungroup();

	  if (debug) {
	    System.out
		.println("GroupTool.mouseLButtonPressed() after ungroup: doc count="
		    + doc.count());
	  }
	}
	canvas.addRepaintShape(tempGroup);
      }
      canvas.repaint();
      g2.dispose();
    }
  }

  @Override
  public void mouseDragged(Observer observer, Point p, int modifiers) {
    // System.out.println("PointTool.mouseLButtonDragged p=" + p);
    if (mouseButtonDown && view != null) {
      displayLocation(p);

      Graphics2D g2 = canvas.getGraphics2D();
      canvas.setRenderingHints(g2);
      canvas.setTransform(g2);
      Point2D cur = view.physicalToVirtual(p);

      if (group != null) {
	// moving an existing group
	float px = (float) cur.getX();
	float py = (float) cur.getY();
	float dx = (float) (px - prev.getX());
	float dy = (float) (py - prev.getY());
	group.moveNotify(dx, dy);

	if (editingGraph && adjacentEdges != null) {
	  GraphDocument.layoutEdges(adjacentEdges);
	}

	if (command == null) {
	  command = new MoveCommand(view, doc, group);
	}
	command.move(dx, dy);
      } else {
	// making a new group
	setGroupRect(start, cur);
      }

      canvas.repaint();
      prev = cur;
      g2.dispose();
    }
  }

  @Override
  public void mouseLButtonReleased(Observer observer, Point p, int modifiers) {
    if (debug) {
      System.out.println("GroupTool.mouseLButtonReleased() p=" + p);
    }

    if (mouseButtonDown && view != null) {

      if (debug) {
	System.out.println("GroupTool.mouseLButtonReleased() @1");
      }

      Point2D cur = view.physicalToVirtual(p);

      Graphics2D g2 = canvas.getGraphics2D();
      canvas.setRenderingHints(g2);
      canvas.setTransform(g2);

      if (group != null) {
	// moving an existing group

	if (debug) {
	  System.out.println("GroupTool.mouseLButtonReleased() @2");
	}

	doc.setGroup(group);
	doc.setSelectedShape(group);
	if (command != null) {

	  if (debug) {
	    System.out.println("GroupTool.mouseLButtonReleased() @2.1");
	  }

	  doc.addCommand(command);
	  command = null;
	}
      } else {
	// make a new group

	if (debug) {
	  System.out.println("GroupTool.mouseLButtonReleased() @3");
	}

	if (!start.equals(cur)) {

	  float x1 = (float) start.getX();
	  float y1 = (float) start.getY();
	  float x2 = (float) cur.getX();
	  float y2 = (float) cur.getY();
	  if (debug) {
	    System.out.println("GroupTool.mouseLButtonReleased() @3.1 " + 
			       x1 + ", " + y1 + ", " + x2 + ", " + y2);
	  }

	  Group group = new RectangularGroup();
	  group.setBounds(x1, y1, x2, y2);
	  doc.makeGroup(group, true);

	  if (debug) {
	    System.out.println("GroupTool.mouseLButtonReleased() @3.1.1 group size=" + group.getShapeCount());
	  }

	  GroupCommand gcmd = new GroupCommand(view, doc, group, prevGroup);
	  doc.executeCommand(gcmd);
	}

	canvas.clearRepaintShapes();
	canvas.repaint();
	g2.dispose();
      }
    }
    mouseButtonDown = false;
  }

  @Override
  public void mouseMoved(Observer observer, Point p, int modifiers) {
    if (view != null) {

      displayLocation(p);

      Point2D cp = view.physicalToVirtual(p);

      Group group = doc.getGroup();
      if (group != null) {
	int pos = group.isInside(cp);
	if (pos == DrawShapeConstants.PointInside) {
	  view.displayHelpMessage(MSGKEY_3);
	  canvas.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
	} else {
	  view.displayHelpMessage(MSGKEY_4);
	  canvas.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	}
      } else {
	view.displayHelpMessage(MSGKEY_1);
	canvas.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    }
  }

  @Override
  public void mouseEntered(Observer observer, Point p, int modifiers) {
    start(observer);
  }

  @Override
  public void keyPressed(Observer observer, int keyChar, int keyCode,
      int modifiers) {
    if (keyCode == KeyEvent.VK_DELETE) {
      Group group = doc.getGroup();
      if (group != null) {
	Command cmd = new RemoveShapeCommand(view, doc, group);
	doc.executeCommand(cmd);
      }
    }
  }

  protected void setGroupRect(Point2D start, Point2D cur) {
    if (start != null && cur != null && tempGroup != null) {
      float x = (float) start.getX();
      float y = (float) start.getY();
      float cx = (float) cur.getX();
      float cy = (float) cur.getY();
      tempGroup.setBounds(x, y, cx, cy);
    }
  }

  protected Point2D start;

  protected RectangularGroup tempGroup;

  protected Group group;

  protected Group prevGroup;

  protected GraphDocument graph;

  protected Set adjacentEdges;

  protected boolean editingGraph;

  protected static final String MSGKEY_1 = "GroupTool.message.1";

  protected static final String MSGKEY_2 = "GroupTool.message.2";

  protected static final String MSGKEY_3 = "GroupTool.message.3";

  protected static final String MSGKEY_4 = "GroupTool.message.4";

  protected DeltaCommand command = null;

  protected static final boolean debug = false;

}
